from pathlib import Path
from datetime import datetime
import logging
import platform
from PIL import Image
from PIL.ExifTags import TAGS
import re

# 配置日志记录器
logger = logging.getLogger(__name__)

class FileMetadataExtractor:
    """文件元数据提取器
    
    使用 pathlib 获取文件的元数据信息，包括创建时间、修改时间等。
    支持跨平台（Windows、Linux、macOS）。
    支持从图片 EXIF 数据中读取拍摄日期。
    支持从视频元数据中读取拍摄日期。
    """
    
    @staticmethod
    def _parse_exif_datetime(datetime_str: str) -> datetime:
        """解析 EXIF 日期时间字符串
        
        Args:
            datetime_str: EXIF 日期时间字符串，格式如 "2025:02:04 16:02:28"
            
        Returns:
            datetime: 解析后的日期时间对象
        """
        try:
            return datetime.strptime(datetime_str, "%Y:%m:%d %H:%M:%S")
        except ValueError:
            return None
    
    @staticmethod
    def _get_exif_datetime(image: Image.Image) -> datetime:
        """从图片 EXIF 数据中获取拍摄日期
        
        按优先级尝试获取以下 EXIF 标签：
        1. DateTimeOriginal - 原始拍摄时间
        2. DateTimeDigitized - 数字化时间
        3. DateTime - 修改时间
        4. CreateDate - 创建时间
        
        Args:
            image: PIL Image 对象
            
        Returns:
            datetime: 拍摄日期时间，如果无法获取则返回 None
        """
        try:
            # 获取 EXIF 数据
            exif = image._getexif()
            if not exif:
                logger.debug("图片没有 EXIF 数据")
                return None
                
            # 按优先级尝试不同的 EXIF 标签
            exif_tags = [
                ('DateTimeOriginal', '原始拍摄时间'),
                ('DateTimeDigitized', '数字化时间'),
                ('DateTime', '修改时间'),
                ('CreateDate', '创建时间')
            ]
            
            for tag_name, tag_desc in exif_tags:
                # 查找标签 ID
                tag_id = None
                for id, name in TAGS.items():
                    if name == tag_name:
                        tag_id = id
                        break
                
                if tag_id and tag_id in exif:
                    data = exif[tag_id]
                    parsed_time = FileMetadataExtractor._parse_exif_datetime(data)
                    if parsed_time:
                        logger.debug(f"从 EXIF 数据获取{tag_desc}: {parsed_time}")
                        return parsed_time
            
            logger.debug("未找到有效的 EXIF 时间信息")
            return None
            
        except Exception as e:
            logger.warning(f"读取 EXIF 数据失败: {str(e)}")
            return None
    
    @staticmethod
    def _extract_datetime_from_filename(filename: str) -> datetime:
        """从文件名中提取日期时间
        
        Args:
            filename: 文件名
            
        Returns:
            datetime: 解析出的日期时间，如果解析失败则返回 None
        """
        # 常见的日期时间模式
        patterns = [
            # 明确的日期格式，带有分隔符
            (r"(?<![0-9])(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}-\d{3})(?![0-9])", "%Y-%m-%d-%H-%M-%S-%f"),  # 2025-03-06-20-26-28-334
            (r"(?<![0-9])(\d{4}-\d{2}-\d{2}-\d{6})(?![0-9])", "%Y-%m-%d-%H%M%S"),  # 2023-08-14-215002
            (r"(?<![0-9])(\d{4}-\d{2}-\d{2})(?![0-9])", "%Y-%m-%d"),  # 2023-08-14
            
            # 下划线分隔的日期时间格式
            (r"(?<![0-9])(\d{8}_\d{6})(?![0-9])", "%Y%m%d_%H%M%S"),  # 20231007_072740
            
            # 连续数字格式
            (r"(?<![0-9])(19[7-9][0-9]|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])([01][0-9]|2[0-3])([0-5][0-9])([0-5][0-9])(?![0-9])", "%Y%m%d%H%M%S"),  # 20210628142507
            (r"(?<![0-9])(19[7-9][0-9]|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(?![0-9])", "%Y%m%d")  # 20170923
        ]
        
        # 获取文件名（不含扩展名）
        name = Path(filename).stem
        
        # 尝试所有模式
        for pattern, date_format in patterns:
            match = re.search(pattern, name)
            if match:
                try:
                    # 使用捕获组来获取日期字符串
                    if len(match.groups()) > 0:
                        date_str = match.group(1)
                    else:
                        date_str = match.group(0)
                        
                    parsed_date = datetime.strptime(date_str, date_format)
                    
                    # 验证年份是否在合理范围内
                    if 1970 <= parsed_date.year <= 2100:
                        return parsed_date
                    else:
                        logger.warning(f"解析出的日期年份超出合理范围: {parsed_date.year}，跳过")
                except ValueError:
                    continue
        
        return None
    
    @staticmethod
    def _get_video_datetime(file_path: str) -> datetime:
        """从视频文件中获取拍摄日期
        
        Args:
            file_path: 视频文件路径
            
        Returns:
            datetime: 拍摄日期时间，如果无法获取则返回 None
        """
        try:
            # 首先尝试从文件名中提取日期
            filename = Path(file_path).name
            datetime_from_filename = FileMetadataExtractor._extract_datetime_from_filename(filename)
            if datetime_from_filename:
                logger.debug(f"从文件名提取到拍摄日期: {datetime_from_filename}")
                return datetime_from_filename
            
            # 如果文件名中没有日期，尝试从视频元数据中获取
            try:
                # 延迟导入 moviepy，只在处理视频时导入
                from moviepy.editor import VideoFileClip
                with VideoFileClip(file_path) as video:
                    # 获取视频的元数据
                    metadata = video.metadata
                    if metadata and 'creation_date' in metadata:
                        try:
                            creation_date = datetime.strptime(metadata['creation_date'], "%Y-%m-%d %H:%M:%S")
                            logger.debug(f"从视频元数据获取拍摄日期: {creation_date}")
                            return creation_date
                        except ValueError:
                            pass
            except ImportError:
                logger.debug("moviepy 模块未安装，跳过视频元数据读取")
            except Exception as e:
                logger.debug(f"读取视频元数据失败: {str(e)}")
            
            return None
                
        except Exception as e:
            logger.warning(f"处理视频文件失败: {str(e)}")
            return None
    
    @staticmethod
    def extract_create_time(file_path: str) -> datetime:
        """提取文件的创建时间
        
        按以下优先级尝试获取时间：
        1. 图片 EXIF 数据中的拍摄日期
        2. 视频元数据中的拍摄日期
        3. 文件名中的日期时间
        4. 文件系统元数据中的创建时间
        5. 文件系统元数据中的修改时间
        
        Args:
            file_path: 文件路径
            
        Returns:
            datetime: 文件的创建时间，如果无法获取则返回 None
        """
        try:
            # 获取文件扩展名
            ext = Path(file_path).suffix.lower()
            logger.debug(f"开始处理文件: {file_path}, 扩展名: {ext}")
            
            # 根据文件类型选择不同的处理方式
            if ext in ['.jpg', '.jpeg', '.png', '.tiff']:
                # 处理图片文件
                logger.debug(f"检测到图片文件: {file_path}")
                try:
                    with Image.open(file_path) as img:
                        logger.debug(f"成功打开图片，格式: {img.format}")
                        if img.format in ['JPEG', 'TIFF', 'PNG']:
                            logger.debug("开始读取 EXIF 数据...")
                            exif_time = FileMetadataExtractor._get_exif_datetime(img)
                            if exif_time:
                                logger.debug(f"成功从 EXIF 数据获取拍摄日期: {exif_time}")
                                return exif_time
                            else:
                                logger.debug("未从 EXIF 数据获取到时间，尝试其他方式")
                        else:
                            logger.debug(f"图片格式 {img.format} 不支持 EXIF 数据")
                except Exception as e:
                    logger.debug(f"读取 EXIF 数据失败: {str(e)}")
                    
            elif ext in ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv', '.webm']:
                # 处理视频文件
                logger.debug(f"检测到视频文件: {file_path}")
                video_time = FileMetadataExtractor._get_video_datetime(file_path)
                if video_time:
                    logger.debug(f"从视频文件获取拍摄日期: {video_time}")
                    return video_time
                else:
                    logger.debug("未从视频文件获取到时间，尝试其他方式")
            
            # 如果上述方法都失败，尝试从文件名中提取日期
            logger.debug(f"尝试从文件名提取日期: {file_path}")
            datetime_from_filename = FileMetadataExtractor._extract_datetime_from_filename(file_path)
            if datetime_from_filename:
                logger.debug(f"从文件名提取到日期: {datetime_from_filename}")
                return datetime_from_filename
            else:
                logger.debug("未从文件名提取到日期，尝试使用文件系统时间")
            
            # 最后尝试使用文件系统时间
            path = Path(file_path)
            stat_info = path.stat()
            
            # 根据操作系统选择合适的时间戳
            if platform.system() == 'Windows':
                # Windows 系统使用 st_ctime 作为创建时间
                timestamp = stat_info.st_ctime
                logger.debug("使用 Windows 系统创建时间")
            else:
                # Linux/macOS 系统使用 st_mtime 作为创建时间
                # 因为 Linux 系统可能没有 st_ctime
                timestamp = stat_info.st_mtime
                logger.debug("使用 Linux/macOS 系统修改时间")
            
            # 将时间戳转换为 datetime 对象
            create_time = datetime.fromtimestamp(timestamp)
            logger.debug(f"从文件系统获取时间: {create_time}")
            return create_time
            
        except Exception as e:
            logger.warning(f"获取文件创建时间失败: {str(e)}")
            return None
    
    @staticmethod
    def extract_modify_time(file_path: str) -> datetime:
        """提取文件的修改时间
        
        Args:
            file_path: 文件路径
            
        Returns:
            datetime: 文件的修改时间，如果无法获取则返回 None
        """
        try:
            # 将字符串路径转换为 Path 对象
            path = Path(file_path)
            
            # 获取文件状态信息
            stat_info = path.stat()
            
            # 获取修改时间
            timestamp = stat_info.st_mtime
            
            # 将时间戳转换为 datetime 对象
            modify_time = datetime.fromtimestamp(timestamp)
            
            logger.debug(f"成功获取文件修改时间: {modify_time}")
            return modify_time
            
        except Exception as e:
            logger.warning(f"获取文件修改时间失败: {str(e)}")
            return None
    
    @staticmethod
    def extract_file_size(file_path: str) -> int:
        """提取文件大小
        
        Args:
            file_path: 文件路径
            
        Returns:
            int: 文件大小（字节），如果无法获取则返回 0
        """
        try:
            # 将字符串路径转换为 Path 对象
            path = Path(file_path)
            
            # 获取文件大小
            size = path.stat().st_size
            
            logger.debug(f"成功获取文件大小: {size} 字节")
            return size
            
        except Exception as e:
            logger.warning(f"获取文件大小失败: {str(e)}")
            return 0 